﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;


using InformationExtraction.Common;
using InformationExtraction.Interface;

namespace InformationExtraction.Utils
{
    //cfj
    class DateExtracter : IFeatureExtractable
    {
        #region IFeatureExtractable 成员
        //
        //Features For Date:
        //DateFormat, MultipleDays, Top1/3,KeyValueFormat, SubmissionNearBy, NotificationNearBy, CameraReadyNearBy, 

        // ConfNameNearBy, LocationNearBy, //pending
        //public List<int> Extract(Token token)
        //{
        //    List<int> fList = new List<int>();
        //    int d = IsDateFormat(token);
        //    int fv1 = 0;
        //    int fv2 = 0;
        //    if (d == 0)
        //    {
        //        int nd = IsDateFormat(token.Next);
        //        int iy;
        //        if (nd == -1)
        //        {
        //            if (Int32.TryParse(token.Content, out iy))
        //                fv1 = 10;
        //        }
        //        int pd = IsDateFormat(token.Prior);
                
        //        if (pd == 2 || pd == 1002)
        //            fv1 = 10;
        //        if (token.Prior != null)
        //        {
        //            int ppd = IsDateFormat(token.Prior.Prior);
        //            if (ppd == 3 || ppd == 1003)
        //                fv1 = 10;
        //        }
        //    }
        //    else 
        //    {
        //        fv1 = 10;
        //        if(d > 1000)
        //            fv2 = 10;
        //    }
        //    fList.Add(fv1);
        //    fList.Add(fv2);
        //    if (IsInTopOneThird(token))
        //        fList.Add(5);
        //    else fList.Add(0);
        //    if (IsKeyValueFormat(token))
        //        fList.Add(1);
        //    else
        //        fList.Add(0);
        //    if (SubmissionNearBy(token))
        //        fList.Add(1);
        //    if (NotificationNearBy(token))
        //        fList.Add(1);
        //    if (CameraReadyNearBy(token))
        //        fList.Add(1);
        //    return fList;
        //}

        public List<int> Extract(Token token)
        {
            List<int> fList = new List<int>();
            string word = token.Content;
            string line = token.InLine.Content;
            //string line = token.InLine.RawContent;
            fList.Add(GetMonth(word));
            fList.Add(GetMultipleDays(word));
            fList.Add(GetDay(word));
            fList.Add(GetYear(word));
            //fList.Add(GetStandardFormat(word));
            fList.Add(GetDateFormat(token));
            fList.Add(GetNotSpecified(line));
            fList.Add(GetMonthNearBy(line));

            fList.Add(GetTopPosition(token));
            return fList;
        }
        public InformationType GetTargetType()
        {
            return InformationType.Date;
            
        }

        #endregion
    

        # region private functions
        private const int NearByLength = 7;
        private bool DateNearBy(Token token)
        {
            string line = token.InLine.Content.ToLower();
            if (line.Contains("date"))
                return true;
            return false;

        }
        private bool IsKeyValueFormat(Token token)
        {
            return token.InLine.IsKeyValue;            
        }
        private bool SubmissionNearBy(Token token)
        {
            if (token.InLine == null)
                return false;
            if (token.InLine.Content == null)
                return false;
            string line = token.InLine.Content.ToLower();
            if (line.Contains("submission") || line.Contains("deadline"))
                return true;
            return false;
        }        
        private bool NotificationNearBy(Token token)
        {
            if (token.InLine == null)
                return false;
            string line = token.InLine.Content.ToLower();
            if (line.Contains("notification") || line.Contains("author") || line.Contains("letter") )
                return true;
            return false;
        }
        private bool CameraReadyNearBy(Token token)
        {
            if (token.InLine == null)
                return false;
            string line = token.InLine.Content.ToLower();
            if (line.Contains("camera") || line.Contains("ready") || line.Contains("final"))
                return true;
            return false;

        }
        private bool IsConfNameNearBy(Token token)
        {
            return true;
        }
        private bool IsLocationNearBy(Token token)
        {
            return true;
        }
        private bool IsInTopOneThird(Token token)
        {
            if (token.InLine == null)
                return false;
            if (token.InLine.No > 0 && token.InLine.No < (token.InLine.InDocument.LineCount / 3 + 2))
                return true;
            return false;
        }
        //return 0: not date
        // return 1: token is a complete date //Nov.11,2009  2009-10-1
        // return 2: token-token is a complete date // Nov. 11,2009  //penidng Nov.11 2009
        // return 3: token-token is a complete date // Nov. 11, 2009
        // return -1: 2009 Nov.11//pending
        //return 1003: multiple days: April 10-13, 2000
        //return 1002: multiple days: April 10-13,2000
        /// <summary>
        /// 
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        private int IsDateFormat(Token token)
        {
            if (token == null)
                return 0;
            DateTime date;
            if (DateTime.TryParse(token.Content, out date))
                return 1;
            string s = token.Content.ToLower();
            if (s.Contains("january") || s.Contains("jan.")
                || s.Contains("february") || s.Contains("feb.")
                || s.Contains("march") || s.Contains("mar.")
                || s.Contains("april") || s.Contains("apr.")
                || token.Content.Contains("May") //|| s.Contains("mar.")
                || s.Contains("june") || s.Contains("jun.")
                || s.Contains("july") || s.Contains("jul.")
                || s.Contains("september") || s.Contains("sept.")
                || s.Contains("august") || s.Contains("aug.")
                || s.Contains("october") || s.Contains("oct.")
                || s.Contains("november") || s.Contains("nov.")
                || s.Contains("december") || s.Contains("dec."))
            {
                Token t;
                do
                {
                   t  = token.Next;
                } while (t == null);
                if(t != null)
                {
                    if (t.Content.Contains("-"))
                    {
                        string[] ts = t.Content.Split('-', ',');
                        int iday;
                        if(ts.Length == 2)
                        {
                            int iday2;
                            if(Int32.TryParse(ts[0], out  iday) && Int32.TryParse(ts[1], out iday2))
                            {
                                if( iday> 0 && iday < 32 && iday2 > 0&& iday2 < 32)
                                {
                                    Token tt = t.Next;
                                    int iyear;
                                    if(Int32.TryParse(tt.Content,out iyear))
                                        return 1003;
                                    return 1002;
                                }
                            }
                        }
                        else if(ts.Length == 3)
                        {
                            int iyear;                        
                            if(Int32.TryParse(ts[2], out iyear))
                                return 1002;
                            if (Int32.TryParse(ts[1], out iday))
                            {
                                if (iday > 0 && iday < 32)
                                    return 1002;
                                return 0; // pending
                            }
                        }                        
                        return 0;                       
                    }
                    return parseDate(t);                    
                }
                return 0;
            }                
            return 0;
        }

        private int parseDate(Token t)
        {
            string[] ts = t.Content.Split(',');
            int iday;
            if (ts.Length == 1)
            {
                if (Int32.TryParse(ts[0], out  iday))
                {
                    if (iday > 0 && iday < 32)
                    {
                        Token tt = t.Next;
                        int iyear;
                        if (Int32.TryParse(tt.Content, out iyear))
                            return 3;
                        return 2;
                    }
                }
            }
            else if (ts.Length == 2)
            {
                int iyear;
                if (Int32.TryParse(ts[1], out iyear))
                    return 2;
                if (Int32.TryParse(ts[0], out iday))
                {
                    if (iday > 0 && iday < 32)
                        return 2;
                    return 0; // pending
                }
            }
            return 0;

        }
        # endregion
        #region ok
        /// <summary>
        /// 提取第一个特征值
        /// 方法：是否是十二个月中的任何一个
        /// </summary>
        /// <param name="text"></param>
        /// <returns>是返回1，不是返回-1</returns>
        private int GetMonth(string word)
        {
            Regex r = new Regex(@"(^|\W)((January)|(February)|(March)|(April)|(May)|(June)|(July)|(August)|(September)|(October)|(November)|(December)|(Jan)|(Feb)|(Mar)|(Apr)|(May)|(Jun)|(Jul)|(Aug)|(Sep)|(Oct)|(Nov)|(Dec))($|\W)", RegexOptions.IgnoreCase);
            if (r.IsMatch(word))
                return 1;

            return -1;
        }

        /// <summary>
        /// 提取第二个特征值
        /// 方法：是否格式为"数字-数字"，且数字介于1-31之间
        /// </summary>
        /// <param name="text"></param>
        /// <returns>是返回1，不是返回-1</returns>
        private int GetMultipleDays(string word)
        {
            Regex r = new Regex(@"^(0?\d|[12]\d|3[01])(st)?(rd)?(nd)?(th)?-(-)?(0?\d|[12]\d|3[01])(st)?(rd)?(nd)?(th)?$");
            if (r.IsMatch(word))
                return 1;
            else
                return -1;
        }
        /// <summary>
        /// 提取第三个特征值
        /// 方法：是否使介于1-31之间的数字
        /// </summary>
        /// <param name="word"></param>
        /// <returns>是返回1，不是返回-1</returns>
        private int GetDay(string word)
        {
            Regex r = new Regex(@"^(0?\d|[12]\d|3[01])(st)?(rd)?(nd)?(th)?(,)?$");
            if (r.IsMatch(word))
                return 1;
            else
                return -1;
        }

        /// <summary>
        /// 提取第四个特征值
        /// 方法：是否是介于1900-2099间的年份
        /// </summary>
        /// <param name="word"></param>
        /// <returns>是返回1，不是返回-1</returns>
        private int  GetYear(string word)
        {
            Regex r = new Regex(@"^\W*(19|20)\d{2}\W*$");
            if (r.IsMatch(word))
                return 1;
            else
                return -1;
        }

        // 是否有其他Attributes的明显字样
        private int GetNotSpecified(string line)
        {
            Regex r = new Regex(@"(submission)|(submit)|(camera)|(notified)|(notification)|(due)|(paper)|(deadline)|(accept)|(author)", RegexOptions.IgnoreCase);
            if (r.IsMatch(line))
                return -1;

            return 1;
        }

       //所在行是否有Month
        private int GetMonthNearBy(string line)
        {
            Regex r = new Regex(@"(January)|(February)|(March)|(April)|(May)|(June)|(July)|(August)|(September)|(October)|(November)|(December)|(Jan)|(Feb)|(Mar)|(Apr)|(May)|(Jun)|(Jul)|(Aug)|(Sep)|(Oct)|(Nov)|(Dec)", RegexOptions.IgnoreCase);
            if (r.IsMatch(line))
                return 1;
            else
                return -1;
        }
        //是否是标准格式的日期
        private int GetStandardFormat(string word)
        {
            DateTime date;
            if (DateTime.TryParse(word, out date))
                return 1;
            else
                return -1;
        }

        private int GetDateFormat(Token token)
        {
            string s = token.Content;//&& GetDay(token.Next.Content) == 1)
            if (GetMonth(s) == 1 || (GetMonthNearBy(token.InLine.Content) == 1 && (GetDay(s) == 1 || GetYear(s) == 1)) || GetStandardFormat(s) == 1)
            {
                //   Console.WriteLine("DateFormat: " +s);
                return 1;
            }
            else
                return -1;
        }
        
        //是否在文档前1/3
        private int GetTopPosition(Token token)
        {         
            if (token.InLine.No >= 0 && token.InLine.No < (token.InLine.InDocument.LineCount / 3 + 2))
                return 1;
            return -1;
        }
        

                
        # endregion
    }
}
